<?php

define('TERRIBLE_SEQUENCE', md5('AF_LINE_BREAK_TMP_098987'));

//FN::import("AFClass.class.php");
include_once "AFClass.class.php";

class AFProgrammingException extends AFException {};

class AFDebugger extends AFClass
{
	public function __construct()
	{
		register_shutdown_function(array($this, 'onShutdown'));
		set_error_handler(array($this, 'onError'));
		set_exception_handler(array($this, 'onException'));
	}

	public function onShutdown()
	{
		$error = error_get_last();

		if(!$error)
		{
			return;
		}

		$this->_printError('Fatal error', $error, NULL, 'error');
	}

	public function onError($type, $message, $file, $line)
	{
		if(!error_reporting())
		{
			// The error has been supressed with an @
			return;
		}
		$this->_printError($this->getFriendlyErrorType($type), array('type'=>$type, 'message'=>$message, 'file'=>$file, 'line'=>$line), NULL, $this->getErrorType($type));
	}

	public function onException($exception)
	{
		$trace = $exception->getTrace();
		if(isset($trace[$exception->getDepth()]))
		{
			$realError = $trace[$exception->getDepth()];
		}
		else
		{
			$realError = array('line'=>$exception->getLine(), 'file'=>$exception->getFile());
		}
		if(!isset($realError['file']))
		{
			throw new AFProgrammingException('Bad exception level');
		}
		$this->_printError('Uncaught exception', array('message'=>'<u>'.get_class($exception).'</u>: '.$exception->getMessage(), 'file'=>$exception->getFile(), 'line'=>$exception->getLine()), $exception->getTrace(), 'error');
//		$this->_printError('Uncaught exception', array('message'=>'<u>'.get_class($exception).'</u>: '.$exception->getMessage(), 'file'=>$realError['file'], 'line'=>$realError['line']));
	}

	private function _printError($what, $error, $trace = NULL, $icon = 'error')
	{
		$errorFile = file_get_contents($error['file']);

		self::debugStart('<h1>'.strtoupper($what).' IN FRAMEWORK</h1>');

		preg_match_all('/^(.*)\/([^\/]+)$/', $error['file'], $out, PREG_SET_ORDER);

		$path = $out[0][1];
		$fileName = $out[0][2];

		$icons = array(
//			'error' => '<img class="af-debugger-icon" alt="" src="" />',
			'error' => '<img cllass="af-debugger-icon" alt="Error" src="" />',
			'warning' => '<img class="af-debugger-icon" alt="Warning" src="" />',
			'info' => '<img class="af-debugger-icon" alt="Notice" src="" />'
		);

		echo '<div class="af-debugger-error-message"><table><tr><td width="1%">'.$icons[$icon].'</td><td><b>'.$what.'</b>: '.$error['message'].'</td></tr></table></div><div class="af-debugger-error-file">'.$path.'/<b class="af-debugger-error-filename">'.$fileName.'</b>:<b>'.$error['line'].'</b></div>';

		echo '<table class="af-debugger-source">';

		str_replace('<br />', TERRIBLE_SEQUENCE, $errorFile);
		$errorFile = highlight_string($errorFile, TRUE);
		$errorFile = explode("<br />", $errorFile);

		for($line = $error['line']-10; $line <= $error['line'] + 10; $line++)
		{
			if($line >= 0 && $line < count($errorFile))
			{
				echo '<tr class="'.(($line == $error['line']-1)?'erroneous':(($line%2)?'even':'odd')).'"><td class="af-debugger-line-number">'.($line+1).'</td><td class="af-debugger-line-code">'.str_replace(TERRIBLE_SEQUENCE, '<br />', $errorFile[$line]).'</td></tr>';
			}
		}

		echo '</table>';

		echo '<div class="af-debugger-error-file">Debug backtrace:</div>';

		echo '<table class="af-debugger-source">';
		$trace = $trace ? $trace : debug_backtrace();
		foreach($trace as $number => $item)
		{
			if(isset($item['class']) && $item['class'] == $this->getClassName())
			{
				continue;
			}

			if(!isset($item['file']))
			{
				$item['file'] = '???';
				$item['line'] = '???';
			}
			preg_match_all('/^(.*)\/([^\/]+)$/', $item['file'], $out, PREG_SET_ORDER);

			if(!isset($item['class']))
			{
				$item['class'] = '<b>[TOP]</b>';
				$item['type'] = '->';
			}

			if(count($out))
			{
				$path = $out[0][1];
				$fileName = $out[0][2];
			}
			else
			{
				$path = '???';
				$fileName = '???';
			}

			echo '<tr class="'.((($number+1) % 2) ? 'even' : 'odd').'"><td class="af-debugger-line-number">'.($number+1).'</td><td width="50%">'.$path.'/<b class="af-debugger-error-filename">'.$fileName.'</b>:<b>'.$item['line'].'</b></td><td>'.$item['class'].$item['type'].$item['function'].'()</td></tr>';
		}
		echo '</table>';

		self::debugEnd();

//		var_dump(error_get_last());7
//		echo 'SHUTDOWN';
	}

	// Function by vladvarna at gmail dot com
	private function getFriendlyErrorType($type)
	{
		switch($type)
		{
			case E_ERROR: // 1 //
				return 'Error';
			case E_WARNING: // 2 //
				return 'Warning';
			case E_PARSE: // 4 //
				return 'Parse error';
			case E_NOTICE: // 8 //
				return 'Notice';
			case E_CORE_ERROR: // 16 //
				return 'Core error';
			case E_CORE_WARNING: // 32 //
				return 'Core warning';
			case E_COMPILE_ERROR: // 64 //
				return 'Compile error';
			case E_COMPILE_WARNING: // 128 //
				return 'Compile warning';
			case E_USER_ERROR: // 256 //
				return 'User error';
			case E_USER_WARNING: // 512 //
				return 'User warning';
			case E_USER_NOTICE: // 1024 //
				return 'User notice';
			case E_STRICT: // 2048 //
				return 'Strict standards';
			case E_RECOVERABLE_ERROR: // 4096 //
				return 'Recoverable error';
			case E_DEPRECATED: // 8192 //
				return 'Deprecated error';
			case E_USER_DEPRECATED: // 16384 //
				return 'User deprecated error';
		}
		return "";
	}

	private function getErrorType($type)
	{
		switch($type)
		{
			case E_ERROR: // 1 //
				return 'error';
			case E_WARNING: // 2 //
				return 'warning';
			case E_PARSE: // 4 //
				return 'error';
			case E_NOTICE: // 8 //
				return 'info';
			case E_CORE_ERROR: // 16 //
				return 'error';
			case E_CORE_WARNING: // 32 //
				return 'warning';
			case E_COMPILE_ERROR: // 64 //
				return 'error';
			case E_COMPILE_WARNING: // 128 //
				return 'warning';
			case E_USER_ERROR: // 256 //
				return 'error';
			case E_USER_WARNING: // 512 //
				return 'warning';
			case E_USER_NOTICE: // 1024 //
				return 'info';
			case E_STRICT: // 2048 //
				return 'warning';
			case E_RECOVERABLE_ERROR: // 4096 //
				return 'error';
			case E_DEPRECATED: // 8192 //
				return 'error';
			case E_USER_DEPRECATED: // 16384 //
				return 'error';
		}
		return "";
	}

	public static function getDebuggerStyle()
	{
		return '
			<style type="text/css">
				.af-debugger-wrap {
					margin: 15px;
				}

				.af-debugger-error {
					width: 100%;
					font-size: 12px;
					font-family: sans-serif;
					box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2);
					border: 1px solid #EEEEEE;
					padding: 1px;
					border-spacing: 0px;
				}

				.af-debugger-error td.af-debugger-error-inside {
					background: #FAFAFA;
					padding: 10px;
				}

				.af-debugger-error a {
					padding: 3px;
					border-radius: 5px;
					margin: 0 -3px;
				}

				.af-debugger-error a:link, .af-debugger-error a:visited {
					color: inherit;
					text-decoration: underline;
				}

				.af-debugger-error a:hover {
					color: #AA0000;
					background: #FFFFFF;
					text-decoration: underline;
				}

				.af-debuffer-error .af-debugger-source {
					font-family: monospace;
				}

				.af-debugger-source {
					border-spacing: 0px;
					width: 100%;
				}

				.af-debugger-source td {
					font-family: monospace;
					font-size: 11px;
				}

				.af-debugger-source td.af-debugger-line-number {
					background: #AAAAAA !important;
					color: #FFFFFF !important;
					padding: 5px;
					aborder-bottom: 1px solid #CCCCCC;
					width: 1px;
					box-shadow: inset 0px 8px 8px rgba(255, 255, 255, 0.2);
				}

				.af-debugger-source td.af-debugger-line-code {
					padding: 5px;
				}

				.af-debugger-source tr.odd td {
					background: #EEEEEE;
				}

				.af-debugger-source tr.erroneous td {
					background: #FFAAAA;
				}

				.af-debugger-error-message table {
					box-shadow: 0px -1px 2px rgba(0, 0, 0, 0.3), inset 0px 16px 16px rgba(255, 255, 255, 0.2);
					background: #AA0000;
					color: #FFFFFF;
					padding: 15px;
				}

				.af-debugger-error-file {
					box-shadow: inset 0px 16px 16px rgba(255, 255, 255, 0.2);
					background: #AAAAAA;
					color: #FFFFFF;
					padding: 5px;
				}

				.af-debugger-error-filename {
					color: #AA2222;
				}

				.af-debugger-icon {
					vertical-align: middle;
				}
			</style>
		';
	}

	public static function debugStart($title)
	{
		echo self::getDebuggerStyle();

		echo '<div class="af-debugger-wrap"><table class="af-debugger-error"><tr><td class="af-debugger-error-inside">'.$title;

		ob_start();
	}

	public static function debugEnd()
	{
		$content = ob_get_contents();
		ob_end_clean();

		echo preg_replace('/\b(AF[a-zA-Z0-9_]*)\b/', '<a href="http://example.com/af/doc.php?wtf=\1">\1</a>', $content);

		echo '</td></tr></table>';
		echo '</div>';
	}
}

?>
